﻿using System;
using System.Collections.Generic;

namespace QSharp.Scheme.Utility.HBSpaceMgmt
{
    public interface IEncodable
    {
        void Encode(IStream stream);
        /**
         * <summary>
         *  return true if decoding successful
         * </summary>
         */
        bool Decode(IStream stream);

        /**
         * <summary>
         *  return the number of bytes encoded
         * </summary>
         */
        ISize EncodedSize { get; }
    }

    public interface IPosition : IEncodable, IComparable<IPosition>, ICloneable
    {
        /**
         * <summary>
         *  A new object must be created as the returned value
         * </summary>
         */
        IPosition Add(ISize size);
        IPosition Subtract(ISize size);
    }

    public interface ISize : IEncodable, IComparable<ISize>, ICloneable
    {
        /**
         * <summary>
         *  A new object must be created as the returned value
         * </summary>
         */
        ISize Add(ISize rhs);
        ISize Subtract(ISize rhs);

        bool IsZero();
    }

    public interface IOperator
    {
        IPosition Add(IPosition lhs, ISize rhs);
        IPosition Subtract(IPosition lhs, ISize rhs);

        ISize Add(ISize lhs, ISize rhs);
        ISize Subtract(ISize lhs, ISize rhs);
    }

    public interface IPaginator : IOperator
    {
        /**
         * <summary>
         *  size of one page measured in paginated scale
         * </summary>
         */
        ISize OnePage { get; }

        /**
         * <summary>
         *  The following two methods convert a scalar object
         *  to corresponding paging object if applicable,
         *  or otherwise returns the object itself.
         *  They are called in section management layer to
         *  spawn critical paging object explicitly at page boundaries
         * </summary>
         * 
         * <remarks>
         *  Paging is not necessary at operator level if pagination 
         *  is performed properly at encoding level, and if merely 
         *  encoding-level pagination can fulfill the requirement.
         * </remarks>
         */
        IPosition Paginate(IPosition pos, ISize pageSize);
        ISize Paginate(ISize size, ISize pageSize);

        /**
         * <summary>
         *  The following two methods function in opposition to 
         *  the two above
         * </summary>
         */
        IPosition Unpaginate(IPosition pos, ISize pageSize);
        ISize Unpaginate(ISize size, ISize pageSize);
    }

    public abstract class BaseOperator : IOperator, IPaginator
    {
        public virtual IPosition Add(IPosition lhs, ISize rhs)
        {
            return lhs.Add(rhs);
        }

        public virtual IPosition Subtract(IPosition lhs, ISize rhs)
        {
            return lhs.Subtract(rhs);
        }

        public virtual ISize Add(ISize lhs, ISize rhs)
        {
            return lhs.Add(rhs);
        }

        public virtual ISize Subtract(ISize lhs, ISize rhs)
        {
            return lhs.Subtract(rhs);
        }

        /* Implementations for IPaginator, beginning of */
        public virtual IPosition Paginate(IPosition pos, ISize pageSize)
        {
            return pos;
        }

        public virtual ISize Paginate(ISize size, ISize pageSize)
        {
            return size;
        }

        public virtual IPosition Unpaginate(IPosition pos, ISize pageSize)
        {
            return pos;
        }

        public virtual ISize Unpaginate(ISize size, ISize pageSize)
        {
            return size;
        }

        public abstract ISize OnePage { get; }

        /* Implementations for IPaginator, end of */
    }

    public interface IStream
    {
        IPosition Position { get; set; }

        void Write(byte[] buffer, int offset, int count);
        int Read(byte[] buffer, int offset, int count);
    }

    public interface ISection
    {
        /**
         * <summary>
         *  Allocate    allocate space of specific size for the 
         *              stream section governed by this section, 
         *              adjust the relevant data structure in 
         *              this section correspondingly; it returns 
         *              null if allocation fails
         *  Deallocate  the reverse to Allocate
         *  
         *  TargetStart starting point of ...
         *  TargetSize  total number of chunks of the inferior 
         *              section governed by this section
         * </summary>
         */
        IPosition Allocate(ISize size);
        void Deallocate(IPosition pos);

        List<IPosition> AllocateForNodes(ISize size);

        IPosition TargetStart { get; }
        ISize TargetSize { get; }
        IOperator TargetOperator { get; }
        IPaginator TargetPaginator { get; }
        ISize TargetPageSize { get; }

        void Decode();
        void Encode();
    }

    public interface IBTreeSection
    {
        /**
         * Convert between position of node and index of the page 
         * containing the node
         */
        IPosition NodePaginate(IPosition pos);
        IPosition NodeUnpaginate(IPosition pos);
    }
}
